/*
 * Cloud9: A MapReduce Library for Hadoop
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you
 * may not use this file except in compliance with the License. You may
 * obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0 
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 * implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

package apweb;

import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Random;
import java.util.StringTokenizer;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.conf.Configured;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapred.KeyValueTextInputFormat;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.Reducer.Context;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.util.Tool;
import org.apache.hadoop.util.ToolRunner;
import org.apache.log4j.Logger;

/**
 * <p>
 * gernate a random data, output, key:number, value:number
 * </p>
 * 
 * <ul>
 * <li>[input-path] input path</li>
 * <li>[output-path] output path</li>
 * <li>[num-reducers] number of reducers</li>
 * </ul>
 * 
 * @author Jimmy Lin
 */
public class MapReduceJoinOverRandomData extends Configured implements Tool {
	private static final Logger sLogger = Logger.getLogger(MapReduceJoinOverRandomData.class);

	// mapper: emits (token, 1) for every word occurrence
	private static class MyMapper extends Mapper<LongWritable, Text, Text, Text> {

		// reuse objects to save overhead of object creation
		Random randomGenerator = new Random();
		String[] tags=null;
		String leftTag = null;
		String rightTag = null;
		int leftIndex=0;
		int rightIndex=0;
		
		@Override
		public void map(LongWritable key, Text value, Context context) throws IOException,
				InterruptedException {
			//
			Configuration conf = context.getConfiguration();
			tags = conf.get("tags").split("\t");
			leftTag = tags[0].trim();
			rightTag = tags[1].trim();
			leftIndex = conf.getInt("leftIndex", 0);
			rightIndex = conf.getInt("rightIndex", 0);

			String[] valuelist = value.toString().split("\t");//column1,column2,column3,tag
			
			context.write(new Text(valuelist[rightIndex]), value );
		}
	}
	
	private static class MyReducer extends Reducer<Text, Text, Text, Text> {

		// reuse objects
		private ArrayList<String> LeftTuples = new ArrayList<String>();
		private ArrayList<String> RightTuples = new ArrayList<String>();
		
		String[] tags=null;
		String leftTag = null;
		String rightTag = null;

		
		@Override
		public void reduce(Text key, Iterable<Text> values,
				Context context) throws IOException, InterruptedException {
			// sum up values
			Configuration conf = context.getConfiguration();
			tags = conf.get("tags").split("\t");
			leftTag = tags[0].trim();
			rightTag = tags[1].trim();
//			return;
			Iterator<Text> iter = values.iterator();
			String value=null;
			LeftTuples.clear();
			RightTuples.clear();
			int number=0;
			while (iter.hasNext()) {
				number++;
				if(number>100)
					break;
				value = iter.next().toString();
				String[] valuelist = value.split("\t");
				if(valuelist[valuelist.length-1].equals(leftTag))
					LeftTuples.add(value);
				else if(valuelist[valuelist.length-1].equals(rightTag))
				{
					RightTuples.add(value);
				}
			}
			for(String lt: LeftTuples)
			{
				for(String rt:RightTuples)
				{
					context.write(key, new Text(ToolsCollections.combine(lt, rt)));
				}
			}
		}
		
	}

	

	/**
	 * Creates an instance of this tool.
	 */
	public MapReduceJoinOverRandomData() {
	}

	private static int printUsage() {
		System.out.println("usage: [left-path] [right-path] [leftIndex,rightIndex] [leftTag,rightTag] [out-path] [num-reducers] ");
		ToolRunner.printGenericCommandUsage(System.out);
		return -1;
	}

	/**
	 * Runs this tool.
	 */
	public int run(String[] args) throws Exception {
		if (args.length != 6) {
			printUsage();
			return -1;
		}

		String leftPath = args[0];
		String rightPath = args[1];
		
		int leftIndex = Integer.parseInt(args[2].split(",")[0]);
		int rightIndex = Integer.parseInt(args[2].split(",")[1]);
		
		String leftTag = args[3].split(",")[0];
		String rightTag = args[3].split(",")[1];
		String outputPath = args[4];
		int reduceTasks = Integer.parseInt(args[5]);

		sLogger.info("Tool: MapReduceJoinOverRandomData");
		sLogger.info(" - input path: " + leftPath+"\t"+rightPath);
		sLogger.info(" - output path: " + outputPath);
		sLogger.info(" - tags: " + leftTag+"\t"+rightTag);
		sLogger.info(" - join: " + leftIndex+"\t"+rightIndex);
		sLogger.info(" - number of reduceTasks: " + reduceTasks);

		Configuration conf = new Configuration();
		
//		conf.setStrings("tags", leftTag, rightTag);
		conf.set("tags", leftTag+"\t"+rightTag);
		conf.setInt("leftIndex", leftIndex);
		conf.setInt("rightIndex", rightIndex);
//		conf.set("mapred.child.java.opts", "-Xmx1024m");

		Job job = new Job(conf, "MapReduceJoinOverRandomData");
		job.setJarByClass(MapReduceJoinOverRandomData.class);

		job.setNumReduceTasks(reduceTasks);

		FileInputFormat.setInputPaths(job, leftPath+","+rightPath);//add input path
		FileOutputFormat.setOutputPath(job, new Path(outputPath));

		job.setOutputKeyClass(Text.class);
		job.setOutputValueClass(Text.class);

		job.setMapperClass(MyMapper.class);
//		job.setCombinerClass(MyReducer.class);
		job.setReducerClass(MyReducer.class);

		// Delete the output directory if it exists already
		Path outputDir = new Path(outputPath);
		FileSystem.get(conf).delete(outputDir, true);

		long startTime = System.currentTimeMillis();
		job.waitForCompletion(true);
		sLogger.info("Job TestMapReduce Finished in " + (System.currentTimeMillis() - startTime) / 1000.0
				+ " seconds");

		return 0;
	}

	/**
	 * Dispatches command-line arguments to the tool via the
	 * <code>ToolRunner</code>.
	 */
	public static void main(String[] args) throws Exception {
		int res = ToolRunner.run(new Configuration(), new MapReduceJoinOverRandomData(), args);
		System.exit(res);
	}
}
